import { Mat4, Quat, Rect, size, Size, v2, v3, Vec2, Vec3 } from 'cc';
import { ecsclass, EcsComponent } from 'db://pkg/@gamex/cc-ecs';

export interface INodeReadonly {
    readonly uuid: number
    readonly isValid: boolean
    readonly ecsName: string
    readonly allowMultiple: boolean
    readonly allowRecycling: boolean

    readonly refreshOpacity: boolean
    readonly refreshAngle: boolean
    readonly refreshSize: boolean
    readonly refreshAnchor: boolean
    readonly refreshScale: boolean
    readonly refreshPosition: boolean
    readonly boundingBox: Readonly<Rect>
    readonly minX: number
    readonly minY: number
    readonly maxX: number
    readonly maxY: number
    readonly rightUpPosition: Vec2
    readonly rightDownPosition: Vec2
    readonly leftUpPosition: Vec2
    readonly leftDownPosition: Vec2

    readonly lastX: number
    readonly lastY: number
    readonly lastZ: number
    readonly lastScaleX: number
    readonly lastScaleY: number
    readonly lastScaleZ: number
    readonly lastAnchorX: number
    readonly lastAnchorY: number
    readonly lastWidth: number
    readonly lastHeight: number
    readonly lastMinX: number
    readonly lastMinY: number
    readonly lastMaxX: number
    readonly lastMaxY: number
    readonly lastRightUpPosition: Vec2
    readonly lastRightDownPosition: Vec2
    readonly lastLeftUpPosition: Vec2
    readonly lastLeftDownPosition: Vec2
}

@ecsclass('NodeComponent')
export class NodeComponent extends EcsComponent {
    static allowRecycling: boolean = true;

    private _refreshOpacity: boolean = false;
    /**刷新透明度 */
    public get refreshOpacity(): boolean {
        return this._refreshOpacity;
    }
    private _refreshAngle: boolean = false;
    /**刷新旋转 */
    public get refreshAngle(): boolean {
        return this._refreshAngle;
    }
    private _refreshSize: boolean = false;
    /**刷新尺寸 */
    public get refreshSize(): boolean {
        return this._refreshSize;
    }
    private _refreshAnchor: boolean = false;
    /**刷新锚点 */
    public get refreshAnchor(): boolean {
        return this._refreshAnchor;
    }
    private _refreshScale: boolean = false;
    /**刷新缩放 */
    public get refreshScale(): boolean {
        return this._refreshScale;
    }
    private _refreshPosition: boolean = false;
    /**刷新位置 */
    public get refreshPosition(): boolean {
        return this._refreshPosition;
    }

    // active
    protected $active = true;
    /**active */
    public get active() {
        return this.$active;
    }
    public set active(value) {
        this.$active = value;
    }

    // 坐标
    protected $position: Vec3 = v3();
    /**x坐标 */
    public get x(): number {
        return this.$position.x;
    }
    public set x(value: number) {
        this._refreshPosition = true;
        this.$position.x = value;
    }
    /**y坐标 */
    public get y(): number {
        return this.$position.y;
    }
    public set y(value: number) {
        this._refreshPosition = true;
        this.$position.y = value;
    }
    /**z坐标 */
    public get z(): number {
        return this.$position.z;
    }
    public set z(value: number) {
        this._refreshPosition = true;
        this.$position.z = value;
    }

    // 缩放
    protected $scale: Vec3 = v3(1, 1, 1);
    /**缩放x */
    public get scaleX(): number {
        return this.$scale.x;
    }
    public set scaleX(value: number) {
        this._refreshScale = true;
        this.$scale.x = value;
    }
    /**缩放y */
    public get scaleY(): number {
        return this.$scale.y;
    }
    public set scaleY(value: number) {
        this._refreshScale = true;
        this.$scale.y = value;
    }
    /**缩放z */
    public get scaleZ(): number {
        return this.$scale.z;
    }
    public set scaleZ(value: number) {
        this._refreshScale = true;
        this.$scale.z = value;
    }

    // 锚点
    protected $anchorPoints: Vec2 = v2(0.5, 0.5);
    /**锚点x */
    public get anchorX(): number {
        return this.$anchorPoints.x;
    }
    public set anchorX(value: number) {
        this._refreshAnchor = true;
        this.$anchorPoints.x = value;
    }
    /**锚点y */
    public get anchorY(): number {
        return this.$anchorPoints.y;
    }
    public set anchorY(value: number) {
        this._refreshAnchor = true;
        this.$anchorPoints.y = value;
    }

    // 尺寸
    protected $contentSize: Size = size();
    /**宽 */
    public get width(): number {
        return this.$contentSize.width;
    }
    public set width(value: number) {
        this._refreshSize = true;
        this.$contentSize.width = value;
    }
    /**高 */
    public get height(): number {
        return this.$contentSize.height;
    }
    public set height(value: number) {
        this._refreshSize = true;
        this.$contentSize.height = value;
    }

    protected $opacity: number = 255;
    /**透明度 */
    public get opacity(): number {
        return this.$opacity;
    }
    public set opacity(value: number) {
        this._refreshOpacity = true;
        this.$opacity = value;

    }

    // 旋转四元数
    protected $lrot = new Quat();
    /**本地坐标系下的旋转，用四元数表示 */
    private get rotation(): Readonly<Quat> {
        return this.$lrot;
    }
    private set rotation(val) {
        Quat.copy(this.$lrot, val);
        Quat.toEuler(this.$euler, this.$lrot);
    }

    // 旋转欧拉角
    protected $euler: Vec3 = v3();
    /**本地坐标系下的旋转，用欧拉角表示 */
    private get eulerAngles(): Readonly<Vec3> {
        return this.$euler;
    }
    private set eulerAngles(val) {
        Vec3.copy(this.$euler, val);
        Quat.fromEuler(this.$lrot, val.x, val.y, val.z);
    }

    /**本地坐标系下的旋转，用欧拉角表示，但是限定在 z 轴上 */
    public get angle(): number {
        return this.$euler.z;
    }
    public set angle(value: number) {
        this._refreshAngle = true;
        this.$euler.z = value;
        Quat.fromAngleZ(this.$lrot, value);
    }

    // 变换矩阵
    private _temp_matrix = new Mat4();  // 临时变量
    /**碰撞盒 */
    get boundingBox(): Readonly<Rect> {
        Mat4.fromRTS(this._temp_matrix, this.$lrot, this.$position, this.$scale);
        const rect = new Rect(
            -this.anchorX * this.width * this.scaleX,
            -this.anchorY * this.height * this.scaleY,
            this.width * this.scaleX,
            this.height * this.scaleY
        );
        rect.transformMat4(this._temp_matrix);
        return rect;
    }

    /**最小x坐标(不计算旋转) */
    get minX() {
        return this.x - this.width * this.anchorX * this.scaleX;
    }
    /**最小y坐标(不计算旋转) */
    get minY() {
        return this.y - this.height * this.anchorY * this.scaleY;
    }
    /**最大x坐标(不计算旋转) */
    get maxX() {
        return this.x + this.width * (1 - this.anchorX) * this.scaleX;
    }
    /**最大y坐标(不计算旋转) */
    get maxY() {
        return this.y + this.height * (1 - this.anchorY) * this.scaleY;
    }

    /**上一帧x坐标 */
    private _lastX: number = null;
    public get lastX(): number {
        if (this._lastX === null) return this.x;
        return this._lastX;
    }
    /**上一帧y坐标 */
    private _lastY: number = null;
    public get lastY(): number {
        if (this._lastY === null) return this.y;
        return this._lastY;
    }
    /**上一帧z坐标 */
    private _lastZ: number = null;
    public get lastZ(): number {
        if (this._lastZ === null) return this.z;
        return this._lastZ;
    }

    /**上一帧缩放x */
    private _lastScaleX: number = null;
    public get lastScaleX(): number {
        if (this._lastScaleX === null) return this.scaleX;
        return this._lastScaleX;
    }
    /**上一帧缩放y */
    private _lastScaleY: number = null;
    public get lastScaleY(): number {
        if (this._lastScaleY === null) return this.scaleY;
        return this._lastScaleY;
    }
    /**上一帧缩放z */
    private _lastScaleZ: number = null;
    public get lastScaleZ(): number {
        if (this._lastScaleZ === null) return this.scaleZ;
        return this._lastScaleZ;
    }

    /**上一帧锚点x */
    private _lastAnchorX: number = null;
    public get lastAnchorX(): number {
        if (this._lastAnchorX === null) return this.anchorX;
        return this._lastAnchorX;
    }
    /**上一帧锚点y */
    private _lastAnchorY: number = null;
    public get lastAnchorY(): number {
        if (this._lastAnchorY === null) return this.anchorY;
        return this._lastAnchorY;
    }

    /**上一帧宽 */
    private _lastWidth: number = null;
    public get lastWidth(): number {
        if (this._lastWidth === null) return this.width;
        return this._lastWidth;
    }
    /**上一帧高 */
    private _lastHeight: number = null;
    public get lastHeight(): number {
        if (this._lastHeight === null) return this.height;
        return this._lastHeight;
    }

    /**上一帧最小x坐标(不计算旋转) */
    get lastMinX() {
        return this.lastX - this.lastWidth * this.lastAnchorX * this.lastScaleX;
    }
    /**上一帧最小y坐标(不计算旋转) */
    get lastMinY() {
        return this.lastY - this.lastHeight * this.lastAnchorY * this.lastScaleY;
    }
    /**上一帧最大x坐标(不计算旋转) */
    get lastMaxX() {
        return this.lastX + this.lastWidth * (1 - this.lastAnchorX) * this.lastScaleX;
    }
    /**上一帧最大y坐标(不计算旋转) */
    get lastMaxY() {
        return this.lastY + this.lastHeight * (1 - this.lastAnchorY) * this.lastScaleY;
    }

    protected onDisable() {
        this.$position.set(0, 0, 0);
        this.$scale.set(1, 1, 1);
        this.$anchorPoints.set(0.5, 0.5);
        this.$contentSize.set(0, 0);
        this.$opacity = 255;
        this.$lrot = new Quat();
        this.$euler.set(0, 0, 0);
        this._lastX = null;
        this._lastY = null;
        this._lastZ = null;
        this._lastScaleX = null;
        this._lastScaleY = null;
        this._lastScaleZ = null;
        this._lastAnchorX = null;
        this._lastAnchorY = null;
        this._lastWidth = null;
        this._lastHeight = null;
        this._refreshOpacity = false;
        this._refreshAngle = false;
        this._refreshSize = false;
        this._refreshAnchor = false;
        this._refreshScale = false;
        this._refreshPosition = false;
    }

    public setPosition(x: number, y: number, z: number = this.z) {
        this.x = x;
        this.y = y;
        this.z = z;
        return this;
    }

    public addPosition(x: number, y: number, z: number = 0) {
        this.x += x;
        this.y += y;
        this.z += z;
        return this;
    }

    public setScale(x: number, y: number, z: number = this.scaleZ) {
        this.scaleX = x;
        this.scaleY = y;
        this.scaleZ = z;
        return this;
    }

    public addScale(x: number, y: number, z: number = 0) {
        this.scaleX += x;
        this.scaleY += y;
        this.scaleZ += z;
        return this;
    }

    public setAngle(angle: number) {
        this.angle = angle;
        return this;
    }

    public addAngle(angle: number) {
        this.angle += angle;
        return this;
    }

    public setContentSize(width: number, height: number) {
        this.width = width;
        this.height = height;
        return this;
    }

    public addContentSize(width: number, height: number) {
        this.width += width;
        this.height += height;
        return this;
    }

    public setAnchorPoints(x: number, y: number) {
        this.anchorX = x;
        this.anchorY = y;
        return this;
    }

    public addAnchorPoints(x: number, y: number) {
        this.anchorX += x;
        this.anchorY += y;
        return this;
    }

    /**
     * 获取上一帧到当前帧的移动量
     */
    public getMovement() {
        return v3(
            this.x - this.lastX,
            this.y - this.lastY,
            this.z - this.lastZ
        );
    }

    /**
     * 获取上一帧到当前帧的移动量
     */
    public getMovementXY() {
        return v2(
            this.x - this.lastX,
            this.y - this.lastY
        );
    }

    /**
     * 获取上一帧到当前帧的移动量
     */
    public getMovementX() {
        return this.x - this.lastX;
    }

    /**
     * 获取上一帧到当前帧的移动量
     */
    public getMovementY() {
        return this.y - this.lastY;
    }

    /**
     * 获取上一帧到当前帧的移动量
     */
    public getMovementZ() {
        return this.z - this.lastZ;
    }

    /**
     * 获取相上一帧到当前帧，对于other的移动量
     */
    public getRelativeMovement(other: NodeComponent) {
        return v3(
            (this.x - this.lastX) - (other.x - other.lastX),
            (this.y - this.lastY) - (other.y - other.lastY),
            (this.z - this.lastZ) - (other.z - other.lastZ)
        );
    }

    /**
     * 获取相上一帧到当前帧，对于other的移动量
     */
    public getRelativeMovementXY(other: NodeComponent) {
        return v2(
            (this.x - this.lastX) - (other.x - other.lastX),
            (this.y - this.lastY) - (other.y - other.lastY)
        );
    }

    /**
     * 获取相上一帧到当前帧，对于other的移动量
     */
    public getRelativeMovementX(other: NodeComponent) {
        return (this.x - this.lastX) - (other.x - other.lastX);
    }

    /**
    * 获取相上一帧到当前帧，对于other的移动量
    */
    public getRelativeMovementY(other: NodeComponent) {
        return (this.y - this.lastY) - (other.y - other.lastY);
    }

    /**
     * 获取相上一帧到当前帧，对于other的移动量
     */
    public getRelativeMovementZ(other: NodeComponent) {
        return (this.z - this.lastZ) - (other.z - other.lastZ);
    }

    /**************************以下为系统调用**************************/
    static systemHandle(node: NodeComponent, reset = true) {
        // 重置refresh
        if (reset) {
            node._refreshAnchor = false;
            node._refreshAngle = false;
            node._refreshOpacity = false;
            node._refreshPosition = false;
            node._refreshSize = false;
            node._refreshScale = false;
        }

        //更新记录上一帧信息
        node._lastX = node.x;
        node._lastY = node.y;
        node._lastZ = node.z;
        node._lastScaleX = node.scaleX;
        node._lastScaleY = node.scaleY;
        node._lastScaleZ = node.scaleZ;
        node._lastAnchorX = node.anchorX;
        node._lastAnchorY = node.anchorY;
        node._lastWidth = node.width;
        node._lastHeight = node.height;
    }
}